/**
  ******************************************************************************
  * @file: oled.c
  * @brief: oled显示函数
  * @author: Mond
  ******************************************************************************
  * @version: 1.0.0
  * @date: 2021/12/5
  * @lastChange: 2021/12/5
  * @history:
        2021/12/5: 编写完成显示字符、字符串、数字、bmp 图像、gif 动画、double
  浮点数
  ******************************************************************************
  */

/* includes */
#include "oled.h"
#include "oledfont.h"
#include <stdio.h>
#include <stdlib.h>

/*!
 * 写命令
 * @param i2c_command 命令
 */
void write_Cmd(unsigned char i2c_command) {
    HAL_I2C_Mem_Write(&hi2c1, OLED0561_ADD, COM, I2C_MEMADD_SIZE_8BIT, &i2c_command, 1, 100);
}

/*!
 * 写数据
 * @param i2c_data 数据
 */
void write_Data(unsigned char i2c_data) {
    HAL_I2C_Mem_Write(&hi2c1, OLED0561_ADD, DAT, I2C_MEMADD_SIZE_8BIT, &i2c_data, 1, 100);
}

/*!
 * OLED 初始化
 */
void oled_Init() {
    HAL_Delay(100);

    write_Cmd(0xAE); // display off
    write_Cmd(0x20); // Set Memory Addressing Mode
    write_Cmd(0x10);
    // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
    write_Cmd(0xb0); // Set Page Start Address for Page Addressing Mode,0-7
    write_Cmd(0xc8); // Set COM Output Scan Direction
    write_Cmd(0x00); //---set low column address
    write_Cmd(0x10); //---set high column address
    write_Cmd(0x40); //--set start line address
    write_Cmd(0x81); //--set contrast control register
    write_Cmd(0xff); //亮度调节 0x00~0xff
    write_Cmd(0xa1); //--set segment re-map 0 to 127
    write_Cmd(0xa6); //--set normal display
    write_Cmd(0xa8); //--set multiplex ratio(1 to 64)
    write_Cmd(0x3F); //
    write_Cmd(0xa4); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
    write_Cmd(0xd3); //-set display offset
    write_Cmd(0x00); //-not offset
    write_Cmd(0xd5); //--set display clock divide ratio/oscillator frequency
    write_Cmd(0xf0); //--set divide ratio
    write_Cmd(0xd9); //--set pre-charge period
    write_Cmd(0x22); //
    write_Cmd(0xda); //--set com pins hardware configuration
    write_Cmd(0x12);
    write_Cmd(0xdb); //--set vcomh
    write_Cmd(0x20); // 0x20,0.77xVcc
    write_Cmd(0x8d); //--set DC-DC enable
    write_Cmd(0x14); //
    write_Cmd(0xaf); //--turn on oled panel
}

/*!
 * OLED 设置光标位置
 * @param x 以左上角为原点，向右方向的坐标，0-127
 * @param y 以左上角为原点，向下方向的坐标，0-7
 */
void oled_Set_Cursor(uint8_t x, uint8_t y) {
    write_Cmd(0xB0 | y);                 // 设置 y 位置
    write_Cmd(0x10 | ((x & 0xF0) >> 4)); // 设置 x 位置低 4 位
    write_Cmd(0x00 | (x & 0x0F));        // 设置 x 位置高 4 位
}

/*!
 * 全屏填充
 * @param fill_data 填充内容
 */
void oled_Fill(uint8_t fill_data) {
    uint8_t i, j;
    for (i = 0; i < 8; i++) {
        oled_Set_Cursor(0, i);
        for (j = 0; j < 128; j++) {
            write_Data(fill_data);
        }
    }
}

/*!
 * 清屏
 */
void oled_Clear() { oled_Fill(0x00); }

/*!
 * OLED 唤醒
 */
void oled_ON() {
    write_Cmd(0x8D); // 设置电荷泵
    write_Cmd(0x14); // 开启电荷泵
    write_Cmd(0xAF); // OLED 唤醒
}

/*!
 * OLED 休眠
 */
void oled_OFF() {
    write_Cmd(0X8D); // 设置电荷泵
    write_Cmd(0X10); // 关闭电荷泵
    write_Cmd(0XAE); // OLED 休眠
}

/*!
 * OLED 显示一个字符
 * @param row 行位置，范围：0~3
 * @param col 列位置，范围：0~15
 * @param ch 要显示的一个字符，范围：ASCII可见字符
 */
void oled_Show_Char(uint8_t row, uint8_t col, char ch) {
    row = row % 4;
    col = col % 16;
    uint8_t i;
    oled_Set_Cursor(col * 8, row * 2); // 设置光标位置在上半部分
    for (i = 0; i < 8; i++) {
        write_Data(OLED_F8x16[ch - ' '][i]); // 显示上半部分内容
    }
    oled_Set_Cursor(col * 8, row * 2 + 1); // 设置光标位置在下半部分
    for (i = 0; i < 8; i++) {
        write_Data(OLED_F8x16[ch - ' '][i + 8]); // 显示下半部分内容
    }
}

/*!
 * OLED 显示字符串
 * @param row 起始行位置，0~3
 * @param col 起始列位置，0~15
 * @param str 要显示的字符串，范围：ASCII可见字符
 */
void oled_Show_String(uint8_t row, uint8_t col, char *str) {
    uint8_t i;
    for (i = 0; str[i] != '\0'; i++) {
        oled_Show_Char(row, col + i, str[i]);
    }
}

/*!
 * OLED 次方函数
 * @param x 底数
 * @param y 指数
 * @return x 的 y 次方
 */
uint32_t oled_Pow(uint32_t x, uint32_t y) {
    uint32_t result = 1;
    while (y--)
        result *= x;
    return result;
}

/*!
 * OLED 显示整数（十进制，无符号）
 * @param row 起始行位置，范围：0~3
 * @param col 起始列位置，范围：0~15
 * @param num 要显示的数字，范围：0~4294967295
 * @param len 要显示数字的长度，范围：1~10
 */
void oled_Show_Number(uint8_t row, uint8_t col, uint32_t num, uint8_t len) {
    uint8_t i;
    for (i = 0; i < len; i++) {
        oled_Show_Char(row, col + i, num / oled_Pow(10, len - i - 1) % 10 + '0');
    }
}

/*!
 * OLED 显示整数（十进制，有符号）
 * @param row 起始行位置，范围：0~3
 * @param col 起始列位置，范围：0~15
 * @param num 要显示的数字，范围：-2147483648~2147483647
 * @param len 要显示数字的长度，范围：1~10
 */
void oled_Show_Signed_Number(uint8_t row, uint8_t col, int32_t num, uint8_t len) {
    uint8_t i;
    uint32_t num_val;
    if (num >= 0) {
        oled_Show_Char(row, col, '+');
        num_val = num;
    } else {
        oled_Show_Char(row, col, '-');
        num_val = -num;
    }
    for (i = 0; i < len; i++) {
        oled_Show_Char(row, col + i + 1, num_val / oled_Pow(10, len - i - 1) % 10 + '0');
    }
}

/*!
 * OLED 显示浮点数（十进制，有符号）
 * @param row 起始行位置，范围：0~3
 * @param col 起始列位置，范围：0~15
 * @param num 要显示的数字，范围：double 范围
 * @param f_num 保留小数位数，范围：1~7
 * @param len 要显示数字的长度，含符号（若为负数），含小数点，范围：1~10
 */
void oled_Show_Float(uint8_t row, uint8_t col, double num, uint8_t f_num, uint8_t len) {
    if (num < 0) {
        oled_Show_Char(row, col, '-');
        col++;
        len--;
        num = -num;
    }
    uint32_t digits = oled_Pow(10, f_num);
    uint32_t tmp = (uint32_t) (num * digits) / digits;
    oled_Show_Number(row, col, tmp, (len - f_num - 1));
    oled_Show_Char(row, col + len - f_num - 1, '.');
    tmp = (uint32_t) (num * digits) % digits;
    oled_Show_Number(row, col + len - f_num, tmp, f_num);
//    free(str);
}

/*!
 * OLED 显示数字（十六进制，无符号）
 * @param row 起始行位置，范围：0~3
 * @param col 起始列位置，范围：0~15
 * @param num 要显示的数字，范围：0~0xFFFF FFFF
 * @param len 要显示数字的长度，范围：1~8
 */
void oled_Show_Hex_Number(uint8_t row, uint8_t col, uint32_t num, uint8_t len) {
    uint8_t i, single_number;
    for (i = 0; i < len; i++) {
        single_number = num / oled_Pow(16, len - i - 1) % 16;
        if (single_number < 10) {
            oled_Show_Char(row, col + i, single_number + '0');
        } else {
            oled_Show_Char(row, col + i, single_number - 10 + 'A');
        }
    }
}

/*!
 * OLED 显示数字（二进制，无符号）
 * @param row 起始行位置，范围：0~3
 * @param col 起始列位置，范围：0~15
 * @param num 要显示的数字，范围：0~0b1111 1111 1111 1111
 * @param len 要显示数字的长度，范围：1~16
 */
void oled_Show_Bin_Number(uint8_t row, uint8_t col, uint32_t num, uint8_t len) {
    uint8_t i;
    for (i = 0; i < len; i++) {
        oled_Show_Char(row, col + i, num / oled_Pow(2, len - i - 1) % 2 + '0');
    }
}

/*!
 * OLED 显示 BMP 图片
 * @param x0 起始列地址，范围 0~127
 * @param y0 起始页地址，范围 0~7
 * @param x1 终止列地址，范围 0~127
 * @param y1 终止页地址，范围 0~7
 * @param bmp 存放图片的数组
 */
void oled_Draw_BMP(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t *bmp) {
    uint32_t k = 0;
    uint8_t i, j;

    for (i = y0; i < y1; i++) {
        oled_Set_Cursor(x0, i);
        for (j = x0; j < x1; j++) {
            write_Data(bmp[k++]);
        }
    }
}

/*!
 * OLED 显示 BMP 图片
 * @param x0 起始列地址，范围 0~127
 * @param y0 起始页地址，范围 0~7
 * @param x1 终止列地址，范围 0~127
 * @param y1 终止页地址，范围 0~7
 * @param frame_num 总帧数
 * @param gif 存放 gif 的数组，1030 x frame_num 个元素。
 */
void oled_Draw_Gif(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t frame_num, const uint8_t gif[][1030]) {
    uint32_t k = 0;
    uint8_t i, j, m;
    for (m = 0; m < frame_num; m++) {
        k = 0;
        for (i = y0; i < y1; i++) {
            oled_Set_Cursor(x0, i);
            for (j = x0; j < x1; j++) {
                write_Data(gif[m][k++]);
            }
        }
        // HAL_Delay(50);
    }
}